package com.tengchong.ohos;

import ohos.agp.components.Attr;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.*;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.global.resource.RawFileEntry;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.Size;

import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This code is copyright (c) 2015 juhuiwan.cn
 * Created by zerob13 on 10/29/15.
 */
public class CircleLoadingView extends Component implements Component.DrawTask {
    private PixelMap mOriginBitmap;
    private PixelMap mResult;
    private Paint mGrayPaint;
    private Paint mArcPaint;
    private Paint mRingPaint;
    private Paint mCirclePaint;
    private Paint mNormalPaint;
    private RectFloat mRectF;
    private int mRingRadius;
    private int mCircleRadius;
    private float mCircleRadiusMax;
    private float mCircleStep;
    private float mCenterX;
    private float mCenterY;
    private float mAnimationDuration;
    private int mCircleStrokeSize;
    private int mPercent;
    private int mAnimationState;

    public CircleLoadingView(Context context) {
        this(context, null);
    }

    public CircleLoadingView(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    public CircleLoadingView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init(attrSet);
    }

    public CircleLoadingView(Context context, AttrSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private void init(AttrSet attrSet) {
        mGrayPaint = new Paint();
        mGrayPaint.setAntiAlias(true);
        ColorMatrix cm = new ColorMatrix();
        cm.setScale(0.382f, 0.382f, 0.382f, 1f);
        mGrayPaint.setColorMatrix(cm);

        mArcPaint = new Paint();
        mArcPaint.setAntiAlias(true);

        mRingPaint = new Paint();
        mRingPaint.setAntiAlias(true);

        mCirclePaint = new Paint();
        mCirclePaint.setAntiAlias(true);

        mNormalPaint = new Paint();
        mNormalPaint.setAntiAlias(true);

        mAnimationState = 2;

        try {
            attrSet.getAttr("cl_circleRadius").ifPresent((Attr attr) -> CircleLoadingView.this.mCircleRadius = attr.getIntegerValue());
            attrSet.getAttr("cl_circleStrokeSize").ifPresent((Attr attr) -> CircleLoadingView.this.mCircleStrokeSize = attr.getIntegerValue());
            attrSet.getAttr("cl_fillAnimationDuration").ifPresent((Attr attr) -> CircleLoadingView.this.mAnimationDuration = attr.getIntegerValue());
            attrSet.getAttr("cl_src").ifPresent((Attr attr) -> CircleLoadingView.this.mOriginBitmap = obtainPixelMap(attr.getStringValue()));
            if (this.mOriginBitmap != null) {
                initRect();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        addDrawTask(this);
    }

    /**
     * 对外暴露设置背景图的方法
     *
     * @param bm
     */
    public void setImageBitmap(PixelMap bm) {
        this.mOriginBitmap = bm;
        initRect();
    }

    private void initRect() {
        int w = this.getWidth();
        int h = this.getHeight();
        if (w == 0 || h == 0) {
            return;
        }
        if (w > 0 && h > 0) {
            if (mCircleRadius < 0) {
                mCircleRadius = mRingRadius = w / 4;
            } else {
                mRingRadius = mCircleRadius;
            }
            mCenterX = w / 2f;
            mCenterY = h / 2f;

            PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
            options.size = new Size(w, h);
            mResult = PixelMap.create(mOriginBitmap, options);

            mRectF = new RectFloat((w / 2 - mCircleRadius), (h / 2 - mCircleRadius),
                    w / 2 + mCircleRadius,
                    h / 2 + mCircleRadius);
            mRingPaint.setStyle(Paint.Style.STROKE_STYLE);
            if (mCircleStrokeSize < 0) {
                mCircleStrokeSize = w / 36;
            }
            mRingPaint.setStrokeWidth(mCircleStrokeSize);
            mCircleRadiusMax = new BigDecimal(Math.sqrt(w * w + h * h)).divide(new BigDecimal(2)).floatValue();
            mCircleStep = (new BigDecimal(mCircleRadiusMax).subtract(new BigDecimal(mCircleRadius))).divide(new BigDecimal(mAnimationDuration).divide(new BigDecimal(25))).floatValue();
            Matrix m = new Matrix();
            RectFloat src = new RectFloat(0, 0, mOriginBitmap.getImageInfo().size.width, mOriginBitmap.getImageInfo().size.height);
            RectFloat dst = new RectFloat(0, 0, this.getWidth(), this.getHeight());
            m.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
            Shader shader = new PixelMapShader(new PixelMapHolder(mOriginBitmap), Shader.TileMode.CLAMP_TILEMODE,
                    Shader.TileMode.CLAMP_TILEMODE);
            shader.setShaderMatrix(m);
            mArcPaint.setShader(shader, Paint.ShaderType.LINEAR_SHADER);
            mRingPaint.setShader(shader, Paint.ShaderType.LINEAR_SHADER);
            mCirclePaint.setShader(shader, Paint.ShaderType.LINEAR_SHADER);
        }
    }

    /**
     * 设置加载进度
     *
     * @param percent
     */
    public void setPercent(int percent) {
        this.mPercent = percent;
        getContext().getUITaskDispatcher().asyncDispatch(this::invalidate);
    }

    /**
     * draw任务
     *
     * @param component
     * @param canvas
     */
    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (mOriginBitmap == null) {
            return;
        }
        int percent = mPercent;
        if (percent == 0) {
            int saveCount = canvas.save();
            canvas.drawPixelMapHolder(new PixelMapHolder(mResult), 0, 0, mGrayPaint);
            canvas.restoreToCount(saveCount);
        } else if (percent < 100) {
            int saveCount = canvas.save();
            canvas.drawPixelMapHolder(new PixelMapHolder(mResult), 0, 0, mGrayPaint);
            canvas.drawCircle(mCenterX, mCenterY, mRingRadius, mRingPaint);
            Arc arc = new Arc(-90, (float) ((percent / 100.0) * 360.0), true);
            canvas.drawArc(mRectF, arc, mArcPaint);
            if (mAnimationState == 2) {
                mAnimationState = 0;
            }
            canvas.restoreToCount(saveCount);
        } else if (percent == 100 && mAnimationState == 0) {
            int saveCount = canvas.save();
            canvas.drawPixelMapHolder(new PixelMapHolder(mResult), 0, 0, mGrayPaint);
            canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mCirclePaint);
            mCircleRadius = new BigDecimal(mCircleRadius).add(new BigDecimal(mCircleStep)).intValue();
            mAnimationState = 1;
            getContext().getUITaskDispatcher().asyncDispatch(this::invalidate);
            canvas.restoreToCount(saveCount);
        } else if (mAnimationState == 1) {
            int saveCount = canvas.save();
            canvas.drawPixelMapHolder(new PixelMapHolder(mResult), 0, 0, mGrayPaint);
            canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mCirclePaint);
            mCircleRadius = new BigDecimal(mCircleRadius).add(new BigDecimal(mCircleStep)).intValue();
            if (mCircleRadius >= mCircleRadiusMax) {
                mAnimationState = 2;
                mCircleRadius = mRingRadius;
            } else {
                getContext().getUITaskDispatcher().asyncDispatch(this::invalidate);
            }
            canvas.restoreToCount(saveCount);

        } else {
            int saveCount = canvas.save();
            canvas.drawPixelMapHolder(new PixelMapHolder(mResult), 0, 0, mNormalPaint);
            canvas.restoreToCount(saveCount);
        }
    }

    /**
     * 通过$media:16777221获取资源
     *
     * @param resStr
     * @return
     */
    private PixelMap obtainPixelMap(String resStr) {
        Pattern pattern = Pattern.compile("[^0-9]");
        Matcher matcher = pattern.matcher(resStr);
        String all = matcher.replaceAll("");
        int resId = Integer.parseInt(all);
        ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();
        ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
        try {
            RawFileEntry rawFileEntry;
            rawFileEntry = getResourceManager().getRawFileEntry(getResourceManager().getMediaPath(resId));
            ImageSource imageSource = ImageSource.create(rawFileEntry.openRawFile(), sourceOptions);
            return imageSource.createPixelmap(decodingOptions);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

